package com.win.oauth.controller;


import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.fyl.core.advance.constant.enums.CommonResponseEnum;
import com.fyl.core.response.RS;
import com.fyl.core.utils.RCache;
import com.fyl.security.anno.OAuthPass;
import com.fyl.validation.anno.ValidationParam;
import com.wf.captcha.base.Captcha;
import com.win.oauth.constant.OAuthConst;
import com.win.oauth.dto.*;
import com.win.oauth.entity.RolePermissionRel;
import com.win.oauth.entity.User;
import com.win.oauth.entity.UserRoleRel;
import com.win.oauth.service.IPermissionService;
import com.win.oauth.service.IRolePermissionRelService;
import com.win.oauth.service.IUserRoleRelService;
import com.win.oauth.service.IUserService;
import com.win.oauth.utils.CaptchaUtil;
import com.win.oauth.utils.JWTUtils;
import com.win.oauth.utils.UserClaims;
import com.win.oauth.utils.UserClaimsHolder;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

@RestController
@RequestMapping("/oauth")
public class LoginController {

    private static final RCache<String> CODE_CACHE = new RCache<>();


    @Autowired
    IUserService userService;

    @Autowired
    IPermissionService permissionService;

    @Autowired
    IRolePermissionRelService rolePermissionRelService;

    @Autowired
    IUserRoleRelService userRoleRelService;

    @OAuthPass
    @PostMapping("/login")
    public RS<Map<String, String>> login(@RequestBody @ValidationParam LoginUserDTO user) {
        CommonResponseEnum.FAIL.assertNotEmpty(user.getPassword());
        CommonResponseEnum.FAIL.assertNotEmpty(user.getFormId());
        CommonResponseEnum.FAIL.assertNotEmpty(user.getCode());

        String validationCode = null;
        try {
            validationCode = CODE_CACHE.get(user.getFormId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (StringUtils.isBlank(validationCode)) {
            return RS.genFail("验证码已失效!");
        }
        if (!Objects.equals(validationCode, user.getCode())) {
            return RS.genFail("验证码错误!");
        }

        String encryptedPwd = DigestUtils.md5Hex(user.getUsername() + user.getPassword());
        User one = userService.lambdaQuery().eq(User::getName, user.getUsername()).one();
        if (null == one || !encryptedPwd.equals(one.getPassword())) {
            return RS.genFail("用户名或密码错误");
        }

        List<UserRoleRel> userRoleRels = userRoleRelService.lambdaQuery().eq(UserRoleRel::getUserId, one.getId()).list();
        List<Long> roleIds = userRoleRels.stream().map(UserRoleRel::getRoleId).collect(toList());

        String token = JWTUtils.sign(one.getId(), roleIds, one.getId() == 1);

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 10);
        String refreshToken = JWTUtils.sign(one.getId(), roleIds, one.getId() == 1, calendar.getTime());

        Map<String, String> map = new HashMap<>();
        map.put("token", token);
        map.put("refreshToken", refreshToken);
        return RS.genSuccess(map);
    }

    @OAuthPass
    @PostMapping("/refreshToken")
    public RS<Map<String, String>> refreshToken(@RequestBody RefreshTokenDTO dto) {

        UserClaims user = JWTUtils.verity(dto.getRefreshToken());
        if (null == user) {
            return RS.genFail(403, CommonResponseEnum.UNAUTHORIZED.getMessage());
        }

        String token = JWTUtils.sign(user.getUserId(), user.getRoleIds(), user.isAdmin());
        Map<String, String> map = new HashMap<>();
        map.put("token", token);
        if (DateUtil.between(user.getIssuedAt(), new Date(), DateUnit.DAY) >= 2) {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, 10);
            String refreshToken = JWTUtils.sign(user.getUserId(), user.getRoleIds(), user.isAdmin(), calendar.getTime());
            map.put("refreshToken", refreshToken);
        }
        return RS.genSuccess(map);
    }

    @OAuthPass
    @GetMapping("/code")
    public RS<Map<String, Object>> code() {

        Captcha captcha = CaptchaUtil.getCaptcha();
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if (captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }

        String uuid = UUID.randomUUID().toString();
        CODE_CACHE.put(uuid, captchaValue);
        // 验证码信息
        Map<String, Object> map = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return RS.genSuccess(map);
    }

    @PostMapping("/logout")
    public RS<Void> logout() {
        return RS.genSuccess();
    }


    @GetMapping("/info")
    public RS<Map<String, Object>> info() {
        UserClaims user = UserClaimsHolder.get();

        List<ModulePermissionDTO> menus = permissionService.listModule();
        List<FunPermissionDTO> funs = permissionService.listFun();

        if (!user.isAdmin()) {

            if (null == user.getRoleIds() || user.getRoleIds().isEmpty()) {
                return RS.genFail(CommonResponseEnum.FORBIDDEN);
            }
            List<RolePermissionRel> list = rolePermissionRelService.lambdaQuery().in(RolePermissionRel::getRoleId, user.getRoleIds()).list();
            Set<Long> permIds = list.stream().map(RolePermissionRel::getPermissionId).collect(toSet());

            Set<String> moduleSubNodeSet = new HashSet<>();
            menus.stream().forEach(el -> {
                if (permIds.contains(el.getPermId())) {
                    String[] nodes = el.getNode().split(OAuthConst.TREE_NODE_DELIMITER);
                    moduleSubNodeSet.addAll(Arrays.asList(nodes));
                }
            });
            funs.stream().forEach(el -> {
                if (permIds.contains(el.getPermId())) {
                    String[] nodes = el.getNode().split(OAuthConst.TREE_NODE_DELIMITER);
                    // 去掉 nodes 最后一个 funSubNode
                    moduleSubNodeSet.addAll(Arrays.asList(nodes).subList(0, nodes.length - 1));
                }
            });
            menus = menus.stream().filter(el -> moduleSubNodeSet.contains(OAuthConst.TREE_NODE_PREFIX + el.getId()) || permIds.contains(el.getPermId())).collect(toList());
            funs = funs.stream().filter(el -> permIds.contains(el.getPermId())).collect(toList());
        }

        User dbUser = userService.getById(user.getUserId());

        Map<String, Object> map = new HashMap<>();
        map.put("name", dbUser.getName());
        map.put("avatar", dbUser.getAvatarUrl());
        map.put("routes", VueRouteDTO.build(menus, funs));
        return RS.genSuccess(map);
    }
}
